package doip.testRepository;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.doip.codec.digitalObject.DigitalObject;
import org.bdware.doip.codec.digitalObject.DoType;
import org.bdware.doip.codec.doipMessage.DoipMessage;
import org.bdware.doip.codec.doipMessage.DoipMessageFactory;
import org.bdware.doip.codec.doipMessage.DoipResponseCode;
import org.bdware.doip.codec.exception.DoDecodeException;
import org.bdware.doip.codec.operations.BasicOperations;
import org.bdware.doip.endpoint.server.DoipServiceInfo;
import org.bdware.doip.endpoint.server.Op;
import org.bdware.doip.endpoint.server.RegistryHandler;
import org.bdware.doip.endpoint.server.RepositoryHandler;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class TestRepoHandler implements RepositoryHandler, RegistryHandler {
    static Logger LOGGER =  LogManager.getLogger(TestRepoHandler.class);

    public AtomicInteger count = new AtomicInteger(0);
    HashMap<String, DigitalObject> digitalObjectHashMap = new HashMap<>();
    public DoipServiceInfo si;

    public TestRepoHandler(DoipServiceInfo serviceInfo){
        si = serviceInfo;
    }

    @Override
    public DoipMessage handleHello(DoipMessage request) {
        LOGGER.info(" handleHello ");
        DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
        DoipMessageFactory.DoipMessageBuilder resp;
        if (request.header.parameters.id.equals(si.id)) {
            resp = builder.createResponse(DoipResponseCode.Success, request);
            resp.setBody(si.toDigitalObject());
        } else {
            resp = builder.createResponse(DoipResponseCode.Invalid, request);
        }
        resp.setRequestID(request.requestID);
        count.incrementAndGet();
        DoipMessage msg = resp.create();
        assert msg.requestID == request.requestID;
        return msg;
    }

    @Override
    public DoipMessage handleListOps(DoipMessage request) {
        LOGGER.info(" handleListOps ");
        DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
        DoipMessageFactory.DoipMessageBuilder resp;
        String targetId = request.header.parameters.id;
        if (digitalObjectHashMap.containsKey(targetId)) {
            resp = builder.createResponse(DoipResponseCode.Success, request);
            List<String> operationIds = new ArrayList<>();
            operationIds.add("0.DOIP/Op.Hello");
            operationIds.add("0.DOIP/Op.Create");
            operationIds.add("0.DOIP/Op.Retrieve");
            operationIds.add("0.DOIP/Op.Update");
            operationIds.add("0.DOIP/Op.Delete");
            operationIds.add("0.DOIP/Op.Search");
            operationIds.add("0.DOIP/Op.ListOperations");

            resp.setBody(new Gson().toJson(operationIds).getBytes(StandardCharsets.UTF_8));

        } else {
            resp = builder.createResponse(DoipResponseCode.DoNotFound, request);
        }
        resp.setRequestID(request.requestID);
        count.incrementAndGet();
        DoipMessage msg = resp.create();
        assert msg.requestID == request.requestID;
        return msg;
    }

    @Override
    public DoipMessage handleCreate(DoipMessage request) {
        LOGGER.info(" handleCreate ");
        DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
        DoipMessageFactory.DoipMessageBuilder resp;
        String targetId ;
        try {
            targetId = request.body.getDataAsDigitalObject().id;
            if (digitalObjectHashMap.containsKey(targetId)) {
                resp = builder.createResponse(DoipResponseCode.DoAlreadyExist, request);
            } else {
                resp = builder.createResponse(DoipResponseCode.Success, request);
                DigitalObject digitalObject = request.body.getDataAsDigitalObject();
                resp.setBody(digitalObject);
                digitalObjectHashMap.put(targetId, digitalObject);
            }
            resp.setRequestID(request.requestID);
            count.incrementAndGet();
            DoipMessage msg = resp.create();
            assert msg.requestID == request.requestID;
            return msg;
        } catch (DoDecodeException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public DoipMessage handleUpdate(DoipMessage request) {
        LOGGER.info(" handleUpdate ");
        LOGGER.info(new Gson().toJson(request));
        DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
        DoipMessageFactory.DoipMessageBuilder resp;
        String targetId = request.header.parameters.id;
        if (!digitalObjectHashMap.containsKey(targetId)) {
            resp = builder.createResponse(DoipResponseCode.DoNotFound, request);
        } else {
            resp = builder.createResponse(DoipResponseCode.Success, request);
            DigitalObject digitalObject = null;
            try {
                digitalObject = request.body.getDataAsDigitalObject();
            } catch (DoDecodeException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            resp.setBody(digitalObject);
            digitalObjectHashMap.replace(targetId, digitalObject);
        }
        resp.setRequestID(request.requestID);
        count.incrementAndGet();
        DoipMessage msg = resp.create();
        assert msg.requestID == request.requestID;
        return msg;
    }

    @Override
    public DoipMessage handleDelete(DoipMessage request) {
        LOGGER.info(" handleDelete ");
        DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
        DoipMessageFactory.DoipMessageBuilder resp;
        String targetId = request.header.parameters.id;
        if (!digitalObjectHashMap.keySet().contains(targetId)) {
            resp = builder.createResponse(DoipResponseCode.DoNotFound, request);
        } else {
            resp = builder.createResponse(DoipResponseCode.Success, request);
            resp.setBody(digitalObjectHashMap.get(targetId));
            digitalObjectHashMap.remove(targetId);
        }
        resp.setRequestID(request.requestID);
        count.incrementAndGet();
        DoipMessage msg = resp.create();
        assert msg.requestID == request.requestID;
        return msg;
    }

    @Override
    public DoipMessage handleRetrieve(DoipMessage request) {
        LOGGER.info(" handleRetrieve ");
        DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
        DoipMessageFactory.DoipMessageBuilder resp;
        String targetId = request.header.parameters.id;
        if (!digitalObjectHashMap.containsKey(targetId)) {
            resp = builder.createResponse(DoipResponseCode.DoNotFound, request);
            DigitalObject dobj = new DigitalObject("targetId", DoType.DO);
            dobj.addAttribute("ResponseCode", "DoNotFound");
            resp.setBody(dobj);
        } else {
            resp = builder.createResponse(DoipResponseCode.Success, request);
            resp.setBody(digitalObjectHashMap.get(targetId));
        }
        resp.setRequestID(request.requestID);
        count.incrementAndGet();
        DoipMessage msg = resp.create();
        assert msg.requestID == request.requestID;
        return msg;
    }

    @Override
    public DoipMessage handleSearch(DoipMessage request) {
        DoipMessageFactory.DoipMessageBuilder builder = new DoipMessageFactory.DoipMessageBuilder();
        DoipMessageFactory.DoipMessageBuilder resp = null;
        String targetId = request.header.parameters.id;
        if (digitalObjectHashMap.containsKey(targetId)) {
            JsonObject result = new JsonObject();
            List<DigitalObject> digitalObjectArrayList = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
                DigitalObject digitalObject = new DigitalObject("number_" + i, DoType.DO);
                digitalObjectArrayList.add(digitalObject);
            }
            result.addProperty("size", 0);
            result.add("results", new Gson().toJsonTree(digitalObjectArrayList));
            resp = builder.createResponse(DoipResponseCode.Success, request);
            resp.setBody(new Gson().toJson(result).getBytes(StandardCharsets.UTF_8));
        } else {
            resp = builder.createResponse(DoipResponseCode.DoNotFound, request);
            DigitalObject dobj = new DigitalObject("targetId", DoType.DO);
            dobj.addAttribute("ResponseCode", "DoNotFound");
            resp.setBody(dobj);
        }
        resp.setRequestID(request.requestID);
        count.incrementAndGet();
        DoipMessage msg = resp.create();
        assert msg.requestID == request.requestID;
        return msg;
    }

    @Op(op = BasicOperations.Extension, name="your_own_extension")
    public DoipMessage handleOwnExtension(DoipMessage request) {
        return null;
    }


}
